package yahoodata;

import java.math.*;
import java.util.*;

/** Values returned by division/multiplication/square-rooting on raw doubles/floats is a "known unknown".
 *  To precisely control arithmatic and maximize acuracy, the BigDecimal class is liberally used, at the
 *  expense of readable code. As this data could be used for real risk modeling, acurate results is more
 *  important than readable code.
 */
public class TradeEvent {
    private static final MathContext DividePrecision  = MathContext.DECIMAL128;
    private static final MathContext DefaultPrecision = MathContext.UNLIMITED;
    
    public final String  symb, date;
    public final float   open, high, low, close, adjClose;
    public final double  volume, dolVolume;
    public final double  closeOpenPriceDiff, highLowPriceDiff;
    public       double  overnightPriceDiff, adjCloseDiff, volumeDiff, dolVolumeDiff; // events are created starting from today, so these diff values
    public       boolean dayBefore;                                                   // are unknowable until the next (previous) event is created.
    
    
    public TradeEvent(String symb, String inline, List<TradeEvent> timeSeries) {
        
        BigDecimal overnightPriceDiff = new BigDecimal(0, DefaultPrecision);  BigDecimal adjCloseDiff  = new BigDecimal(0, DefaultPrecision);
        BigDecimal volumeDiff         = new BigDecimal(0, DefaultPrecision);  BigDecimal dolVolumeDiff = new BigDecimal(0, DefaultPrecision);
        
        this.symb = symb;
        StringTokenizer split = new StringTokenizer(inline, ",");
        this.date     = (String) split.nextToken();
        String openString     = (String) split.nextToken();
        String highString     = (String) split.nextToken();
        String lowString      = (String) split.nextToken();
        String closeString    = (String) split.nextToken();
        String volumeString   = (String) split.nextToken();
        String adjCloseString = (String) split.nextToken();
        
        this.open     = Float.valueOf(openString);
        this.high     = Float.valueOf(highString);
        this.low      = Float.valueOf(lowString);
        this.close    = Float.valueOf(closeString);
        this.volume   = Double.valueOf(volumeString);
        this.adjClose = Float.valueOf(adjCloseString);
        
        BigDecimal openObj     = new BigDecimal(openString,     DefaultPrecision);
        BigDecimal highObj     = new BigDecimal(highString,     DefaultPrecision);
        BigDecimal lowObj      = new BigDecimal(lowString,      DefaultPrecision);
        BigDecimal closeObj    = new BigDecimal(closeString,    DefaultPrecision);
        BigDecimal adjCloseObj = new BigDecimal(adjCloseString, DefaultPrecision);
        BigDecimal volumeObj   = new BigDecimal(volumeString,   DefaultPrecision);
        
        BigDecimal dolVolumeObj = adjCloseObj.multiply(volumeObj, DefaultPrecision);
        this.dolVolume          = dolVolumeObj.doubleValue();
        
        if(openObj.doubleValue() != 0) {
            this.closeOpenPriceDiff = ((BigDecimal) (closeObj.subtract(openObj, DefaultPrecision)).divide(openObj, DividePrecision)).doubleValue();
        } else {
            this.closeOpenPriceDiff = 0;
        }
        if(lowObj.doubleValue() != 0) {
            this.highLowPriceDiff   = ((BigDecimal) (highObj.subtract(lowObj, DefaultPrecision)).divide(lowObj, DividePrecision)).doubleValue();
        } else {
            this.highLowPriceDiff = 0;
        }
        
        this.overnightPriceDiff = 0;
        this.adjCloseDiff       = 0;
        this.volumeDiff         = 0;
        this.dolVolumeDiff      = 0;
        this.dayBefore          = false;

        if(timeSeries.size() > 0) {
            TradeEvent te = (TradeEvent) timeSeries.get((timeSeries.size() - 1));
            BigDecimal _openObj      = new BigDecimal(te.open,      DefaultPrecision);  // to know the price change, you need to know today's price
            BigDecimal _adjCloseObj  = new BigDecimal(te.adjClose,  DefaultPrecision);  // as well as yesterdays's price. thus, price change is unknowable
            BigDecimal _volumeObj    = new BigDecimal(te.volume,    DefaultPrecision);  // until both values are known. events are ordered starting with the most
            BigDecimal _dolVolumeObj = new BigDecimal(te.dolVolume, DefaultPrecision);  // recent date. thus, one must "look back" to an already created object to
                                                                                        // to define that objects' price/overnight/etc. change
            if(adjCloseObj.doubleValue() != 0) {
                te.adjCloseDiff       = ((BigDecimal) _adjCloseObj.subtract(adjCloseObj, DefaultPrecision).divide(adjCloseObj, DividePrecision)).doubleValue();
            } else { te.adjCloseDiff = 0; }
            if(closeObj.doubleValue() != 0) {
                te.overnightPriceDiff = ((BigDecimal) _openObj.subtract(closeObj, DefaultPrecision).divide(closeObj, DividePrecision)).doubleValue();
            } else {te.overnightPriceDiff = 0; }
            if(volumeObj.doubleValue() != 0) {
                te.volumeDiff    = ((BigDecimal) _volumeObj.subtract(volumeObj, DefaultPrecision).divide(volumeObj, DividePrecision)).doubleValue();
            } else { te.volumeDiff = 0; }
            if(dolVolumeObj.doubleValue() != 0) {
                te.dolVolumeDiff = ((BigDecimal) _dolVolumeObj.subtract(dolVolumeObj, DefaultPrecision).divide(dolVolumeObj, DividePrecision)).doubleValue();
            } else {
                te.dolVolumeDiff = 0;
            }
            te.dayBefore = true;
        }        
    }
    
}
